#!/usr/bin/env python
# -*- coding: utf-8 -*-
'''
Created on 2013-9-5

most codes  come from https://github.com/ablanco/python-youtube-download
@author: iori
'''


import re
from urllib import unquote, urlopen, urlencode
from urlparse import parse_qs
from utils import safe_filename
from video import Video
YT_BASE_URL = 'http://www.youtube.com/get_video_info'

#YouTube quality and codecs id map.
#source: http://en.wikipedia.org/wiki/YouTube#Quality_and_codecs
YT_ENCODING = {
    #Flash Video
    5: ["flv", "240p", "Sorenson H.263", "N/A", "0.25", "MP3", "64"],
    6: ["flv", "270p", "Sorenson H.263", "N/A", "0.8", "MP3", "64"],
    34: ["flv", "360p", "H.264", "Main", "0.5", "AAC", "128"],
    35: ["flv", "480p", "H.264", "Main", "0.8-1", "AAC", "128"],

    #3GP
    36: ["3gp", "240p", "MPEG-4 Visual", "Simple", "0.17", "AAC", "38"],
    13: ["3gp", "N/A", "MPEG-4 Visual", "N/A", "0.5", "AAC", "N/A"],
    17: ["3gp", "144p", "MPEG-4 Visual", "Simple", "0.05", "AAC", "24"],

    #MPEG-4
    18: ["mp4", "360p", "H.264", "Baseline", "0.5", "AAC", "96"],
    22: ["mp4", "720p", "H.264", "High", "2-2.9", "AAC", "192"],
    37: ["mp4", "1080p", "H.264", "High", "3-4.3", "AAC", "192"],
    38: ["mp4", "3072p", "H.264", "High", "3.5-5", "AAC", "192"],
    82: ["mp4", "360p", "H.264", "3D", "0.5", "AAC", "96"],
    83: ["mp4", "240p", "H.264", "3D", "0.5", "AAC", "96"],
    84: ["mp4", "720p", "H.264", "3D", "2-2.9", "AAC", "152"],
    85: ["mp4", "520p", "H.264", "3D", "2-2.9", "AAC", "152"],

    #WebM
    43: ["webm", "360p", "VP8", "N/A", "0.5", "Vorbis", "128"],
    44: ["webm", "480p", "VP8", "N/A", "1", "Vorbis", "128"],
    45: ["webm", "720p", "VP8", "N/A", "2", "Vorbis", "192"],
    46: ["webm", "1080p", "VP8", "N/A", "N/A", "Vorbis", "192"],
    100: ["webm", "360p", "VP8", "3D", "N/A", "Vorbis", "128"],
    101: ["webm", "360p", "VP8", "3D", "N/A", "Vorbis", "192"],
    102: ["webm", "720p", "VP8", "3D", "N/A", "Vorbis", "192"]
}

# The keys corresponding to the quality/codec map above.
YT_ENCODING_KEYS = (
    'extension', 'resolution', 'video_codec', 'profile', 'video_bitrate',
    'audio_codec', 'audio_bitrate'
)


class YouTube(object):
    
    def __init__(self):
        ''' fmt was an undocumented URL parameter that allowed selecting
        YouTube quality mode without using player user interface.'''        
        self._filename = ''
        self._videos = []
        self._video_url  = ''
        self.title = ''
        self._fmt_values = []
        
    @property
    def url(self):
        return self._video_url
    @url.setter
    def url(self, url):
        self._video_url = url
        self._filename = ''
        self._get_video_info()
        
    @property
    def filename(self):
        if not self._filename:
            self._filename = safe_filename(self.title)
        return self._filename
    @filename.setter
    def filename(self, filename):
        self._filename = filename
        if self._videos:
            for video in self._videos:
                video.filename = filename
    @property
    def video_id(self):
        vid = re.findall(r'v=(.+)', self._video_url)[0]
        return vid
    def _parse_fmt_stream_map(self, text):
        '''         
        Python's `parse_qs` can't properly decode the stream map
        containing video data so we use this instead.
        Keyword arguments:
        data -- The parsed response from YouTube.
        ''' 
        fmt = text['url_encoded_fmt_stream_map'][0]
        videos = fmt.split(',')
        video_info = {}
        for video in videos:
            for param in video.split('&'):
                k, v = param.split('=')
                if not k in video_info:
                    video_info[k] = []
                video_info[k].append(unquote(v))
        return video_info
    def _extract_fmt(self, text):
        '''
        YouTube does not pass you a completely valid URLencoded form,
        I suspect this is suppose to act as a deterrent.. Nothing some
        regulular expressions couldn't handle.

        Keyword arguments:
        text -- The malformed data contained within each url node.
        '''
        itag = re.findall(r'itag=(\d+)', text)
        if itag :
            itag = int(itag[0])
            attr= YT_ENCODING.get(itag, None)
            if not attr:
                return itag, None
            data = {}
            map(lambda k, v: data.update({k:v}), YT_ENCODING_KEYS, attr)
            return itag, data
    def _dump_video_info(self):
        for v in self._videos:
            print v.resolution, v.extension
                
    def _get_video_info(self):
        qs = urlencode({'asv': 3, 'el': 'detailpage', 'hl': 'en_US',
                             'video_id': self.video_id})
        proxies = {'http':'http://127.0.0.1:8087'}
        url = YT_BASE_URL + '?' + qs
        res = urlopen(url, proxies = proxies)
        if res:
            info = parse_qs(res.read())   # python's parse_qs can not parse it correctly
            if 'errorcode' in info:
                raise 
            stream_map = self._parse_fmt_stream_map(info)
            video_urls = stream_map["url"]
            #Get the video signatures, YouTube require them as an url component
            video_signatures = stream_map["sig"]
            self.title = info['title'][0]
            for i in range(len(video_urls)):
                url = video_urls[i]
                sig = video_signatures[i]
                try:
                    itag, data = self._extract_fmt(url)
                except:
                    pass
                else:
                    if not data: continue
                    url = '%s&signature=%s'%(url, sig)
                    v = Video(url, self.filename, **data)
                    self._videos.append(v)
            self._videos.sort(reverse=True)
#             self._dump_video_info()
    def filter(self, extension=None, resolution=None):
        ''' return video object
            extension; video extension like mp4, webm, flv etc.
            resolution:  The desired broadcasting standard of the video (e.g.: 1080p)
        '''
        res = []
        for v in self._videos:
            if extension and v.extension != extension: continue
            if resolution and v.resolution != resolution: continue
            res.append(v)
        return res
    def get(self, extension=None, resolution=None):
        res = self.filter(extension, resolution)
        if res: 
            for v in res:
                if v['profile'] != '3D': # 3D not needed
                    return v
        else: # have video, but not satisfy our requirement
            resolution  = int(resolution[:-2])
            for v in self._videos:
                if int(v.resolution[:-2])<= resolution :
                    return v
            
                